package cn.visu.security.filter;


import cn.visu.bean.Token;
import cn.visu.service.TokenService;
import com.alibaba.fastjson.JSONObject;
import com.visu.Constants;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import cn.visu.security.util.JwtUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 身份验证过滤器
 * 用于主体认证的
 */
public class MyAuthenticationFilter extends AuthenticatingFilter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private TokenService tokenService;

    @Override
    protected boolean onAccessDenied(ServletRequest var1, ServletResponse response) throws Exception {
        //验证失败后的处理
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        try {
            WebUtils.toHttp(response).sendError(Constants.FAIL, "验证失败，请重新登陆！");
        } catch (Exception e1) {
            //e1.printStackTrace();
        }
        return false;
    }

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {

        return null;
    }

    /**
     * 用token实现身份验证
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        logger.debug("isAccessAllowed  param(request):{}", request);
        HttpServletRequest var2 = WebUtils.toHttp(request);
        String method = var2.getMethod();
        String str = var2.getHeader("Access-Control-Request-Headers");
        if (method.equals("OPTIONS")) {
            if ("authentication".equals(str)) {
                WebUtils.toHttp(response).setStatus(202);
                return true;
            }
        }
        boolean result = false;
        String token = getAuthzHeader(request);
        String username;
        String sessionId;
        try {
            JSONObject object = jwtUtils.getFromBase64(token.split("\\.")[1]);
            username = object.getString("sub");
            sessionId = object.getString("jti");
        } catch (Exception e) {
            try {
                WebUtils.toHttp(response).sendError(Constants.TOKEN_INVALID, "token invalid");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return false;
        }
        //根据token中的username和sessionId创建subject，与上一次请求构成一次会话
        Subject subject = createSubject(username, sessionId);
        //验证token是否与用户token表中的token一致，保证一个用户同一时间只能登陆一次
        if (!StringUtils.isEmpty(username) && verifyJWT(username, token)) {
            try {
                jwtUtils.parseJWT(token);//验证token的有效性
            } catch (Exception e) {
                //subject.logout();
                try {
                    WebUtils.toHttp(response).sendError(Constants.TOKEN_INVALID, "token invalid");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                return false;
            }
            result = subject.isAuthenticated();
        } else {
            try {
                //subject.logout();
                WebUtils.toHttp(response).sendError(Constants.TOKEN_INVALID, "token invalid");
            } catch (IOException e) {
                e.printStackTrace();
            }
            result = false;
        }
        logger.info("isAccessAllowed  result:{}", result);
        if (result) subject.getSession().touch(); //刷新session
        return result;
    }

    private boolean verifyJWT(String username, String token) {
        Token tk = tokenService.findTokenByUserName(username);
        if (tk == null || !tk.getToken().equals(token)) {
            return false;
        }
        return true;
    }

    public Subject createSubject(String username, String sessionId) {
        String realmName = JdbcRealm.class.getName();
        PrincipalCollection principals = new SimplePrincipalCollection(username, realmName);
        Subject subject = new Subject.Builder().principals(principals).sessionId(sessionId).buildSubject();
        ThreadContext.bind(subject);
        return subject;
    }

    protected String getAuthzHeader(ServletRequest var1) {
        HttpServletRequest var2 = WebUtils.toHttp(var1);
        return var2.getHeader("Authentication");
    }

}
